`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2017/11/02 14:52:16
// Design Name: 
// Module Name: alu
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module alu (
    input wire [31:0] a,
    b,
    hi,
    lo,
    input wire [4:0] sa,
    input wire [5:0] op,
    output reg [31:0] y,
    output reg overflow,
    output wire zero,
    output reg start_div,  // 除了除法计算置1，其余的都为其置0
    output reg div_sign,  // 有符号除法置1
    output reg [31:0] hi_out,
    lo_out
);

  reg [31:0] tmp, tmp1;

  reg [63:0] mult_tmp;

  always @(*) begin
    case (op)
      6'd0:  y <= a & b;
      6'd1:  y <= a | b;
      6'd2:  y <= a + b;
      6'd3: begin
        tmp <= a + b;
        y   <= tmp[31];
      end
      6'd4:  y <= a - b;  //sub
      6'd5: begin
        tmp <= a - b;
        y   <= tmp[31];
      end  //slt
      6'd6: begin
        tmp <= {16'b0, b[15:0]};
        y   <= a & tmp;
      end  // ANDI
      6'd7: begin
        tmp <= {16'b0, b[15:0]};
        y   <= a ^ tmp;
      end  // XORI
      6'd8: begin
        tmp <= {b[15:0], 16'b0};
        y   <= tmp;
      end  //LUI
      6'd9: begin
        tmp <= {16'b0, b[15:0]};
        y   <= a | tmp;
      end  // ORI
      6'd10: y <= a ^ b;  // XOR
      6'd11: y <= ~(a | b);  // NOR
      6'd12: y <= b << sa;  // SLL
      6'd13: y <= b >> sa;  // SRL
      6'd14: y <= $signed(b) >>> sa;  // SRA
      6'd15: y <= b << a;  // SLLV
      6'd16: y <= b >> a;  // SRLV
      6'd17: y <= $signed(b) >>> a;  // SRAV
      6'd18: begin
        div_sign <= 1;
      end  // DIV
      6'd19: begin
        div_sign <= 0;
      end  // DIVU
      6'd20: begin
        hi_out <= a;
      end  //MTHI
      6'd21: begin
        lo_out <= a;
      end  //MTLO
      6'd22: y <= lo;  //MFLO
      6'd23: y <= hi;  //MFHI

      6'd24:   y <= a + b;  //ADDU
      6'd25:   y <= a - b;  //SUBU
      6'd26: begin
        tmp <= a - b;
        y   <= tmp[31];
      end  //SLTU
      6'd27: begin
        // tmp <= {{32{a[31]}}, a};
        // tmp1 <= {{32{b[31]}}, b};
        mult_tmp <= $signed(a) * $signed(b);
        hi_out   <= mult_tmp[63:32];
        lo_out   <= mult_tmp[31:0];
      end  //MULT
      6'd28: begin
        tmp <= {32'b0, a};
        tmp1 <= {32'b0, b};
        mult_tmp <= tmp1 * tmp;
        hi_out <= mult_tmp[63:32];
        lo_out <= mult_tmp[31:0];
      end  //MULTU，先不考虑溢出
      6'd29: begin
        y <= a + 4;
      end  // PC+8
      6'd30: begin
        y <= b;
      end
      default: y <= 32'b0;
    endcase

    // 单独控制除法
    case (op)
      6'd18, 6'd19: start_div <= 1;
      default: start_div <= 0;
    endcase
  end
  assign zero = (y == 32'b0);

  // 溢出检测
  always @(*) begin
    case (op)
      6'd2, 6'd3: overflow <= a[31] & b[31] & ~y[31] | ~a[31] & ~b[31] & y[31];
      6'd4, 6'd5: overflow <= ~a[31] & b[31] & y[31] | a[31] & ~b[31] & ~y[31];
      default: overflow <= 1'b0;
    endcase
  end
endmodule
